import { Scene } from "@babylonjs/core/scene";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera';
import { Vector2, Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Camera } from '@babylonjs/core/Cameras/camera';
import { RenderTargetTexture } from "@babylonjs/core/Materials/Textures/renderTargetTexture";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { Color4 } from "@babylonjs/core/Maths/math.color";
import { Helper } from "../tool/helper";


/**
 * 细节视图参数对象
 * @param body 渲染容器网格
 * @param container 容器
 * @param size 尺寸大小
 * @param tessellation 渲染容器分段数
 * @param billboard 朝向模式
 * @param cameraAlpha 相机方位角
 * @param cameraBeta 相机极角
 * @param cameraRadius 相机半径
 * @param cameraTarget 相机焦点
 * @param textureResolution 纹理分辨率（不宜过高）
 * @param renderTargetTextureUAngle 纹理U旋转角度
 * @param renderTargetTextureVAngle 纹理V旋转角度
 * @param renderTargetTextureWAngle 纹理W旋转角度
 * @param sceneClearColor 场景底色
 * @param hasAlpha 是否有alpha通道
 * @param camera 相机
 * @param forceUpdateActiveCamera 更新场景活动相机
 * @param refreshRate 刷新率
 */
interface IDetailViewOptions {
    body?: AbstractMesh,
    container?: TransformNode,
    size?: number,
    tessellation?: number,
    billboard?: number,
    cameraAlpha?: number,
    cameraBeta?: number,
    cameraRadius?: number,
    cameraTarget?: Vector3,
    textureResolution?: Vector2,
    renderTargetTextureUAngle?: number,
    renderTargetTextureVAngle?: number,
    renderTargetTextureWAngle?: number,
    sceneClearColor?: Color4,
    hasAlpha?: boolean,
    camera?: Camera,
    forceUpdateActiveCamera?: boolean,
    refreshRate?: number
}


/**
 * 细节视图
 * @param name 名称
 * @param scene 所在场景
 * @param targetScene 目标场景
 * @param options 参数对象
 */
class DetailView {

    private _name: string;
    private _body: AbstractMesh;
    private _scene: Scene;
    private _targetScene: Scene;
    private _targetCamera!: Camera;
    private _renderTargetTexture!: RenderTargetTexture;

    constructor(name: string, scene: Scene, targetScene?: Scene, options?: IDetailViewOptions) {
        const ops = options || {};
        this._name = name;
        this._scene = scene;
        this._scene.renderTargetsEnabled = true;
        this._targetScene = this.initializeScene(targetScene, ops);
        this._targetCamera = this.initializeCamera(ops);
        this._body = this.initializeBody(ops);
    }

    /**
     * 获取名称
     */
    public get name(): string {
        return this._name;
    }

    /**
     * 获取渲染网格容器
     */
    public get body(): AbstractMesh {
        return this._body;
    }

    /**
     * 获取目标相机
     */
    public get targetCamera(): Camera {
        return this._targetCamera;
    }

    /**
     * 获取目标场景
     */
    public get targetScene(): Scene {
        return this._targetScene;
    }

    /**
     * 获取渲染对象纹理
     */
    public get renderTargetTexture(): RenderTargetTexture {
        return this._renderTargetTexture;
    }

    /**
     * 
     * @param targets 添加渲染对象
     */
    public addRenderTarget(...targets: Array<TransformNode>): void {
        if (!this._renderTargetTexture.renderList) this._renderTargetTexture.renderList = [];
        for (const tar of targets) {
            const ms = Helper.getChildMeshes(tar);
            for (const m of ms) {
                if (m.uniqueId === this._body.uniqueId) continue;
                this._renderTargetTexture.renderList.push(m);
            }
        }
    }

    /**
     * 窗体大小改变时的响应函数
     */
    public windowResizeUpdate(): void {
        const scene = this._scene;
        const rtt = this._renderTargetTexture;
        const asp = 1 / scene.getEngine().getAspectRatio(this._targetCamera);
        if (asp <= 1) {
            rtt.vScale = 1;
            rtt.vOffset = 0;
            rtt.uScale = asp;
            rtt.uOffset = (1 - asp) * 0.5;
        } else {
            rtt.uScale = 1;
            rtt.uOffset = 0;
            rtt.vScale = 1 / asp;
            rtt.vOffset = (1 - 1 / asp) * 0.5;
        }
    }

    /**
     * 初始化目标场景
     * @param targetScene 目标场景
     * @param options 参数对象
     * @returns 场景
     */
    private initializeScene(targetScene: Scene | undefined | null, options: IDetailViewOptions): Scene {
        return targetScene || this.generateScene(options);
    }

    /**
     * 创建场景
     * @param options 参数对象
     * @returns 场景
     */
    protected generateScene(options: IDetailViewOptions): Scene {
        const scene = new Scene(this._scene.getEngine());
        scene.clearColor = options.sceneClearColor || new Color4(0, 0, 0, 0);
        return scene;
    }

    /**
     * 初始化相机
     * @param options 参数对象
     * @returns 相机
     */
    private initializeCamera(options: IDetailViewOptions): Camera {
        const camera = options.camera || this._targetScene.activeCamera || this.generateCamera(options);

        if (options.forceUpdateActiveCamera) {
            if (this._targetScene.activeCameras) {
                const cms = this._targetScene.activeCameras;
                for (let i = 0; i < cms.length; i++) {
                    if (cms[i].uniqueId === camera.uniqueId) {
                        cms.splice(i, 1);
                        i--;
                    }
                }
                this._targetScene.activeCameras.push(camera);
            } else {
                this._targetScene.activeCameras = [camera];
            }
            this._targetScene.activeCamera = camera;
        }
        return camera;
    }

    /**
     * 创建相机
     * @param options 参数对象
     * @returns 相机
     */
    protected generateCamera(options: IDetailViewOptions): Camera {
        const camera = new ArcRotateCamera(
            this._name + '-target-camera',
            options.cameraAlpha || 0,
            options.cameraBeta === undefined ? Math.PI * 0.5 : options.cameraBeta,
            options.cameraRadius || 10,
            options.cameraTarget || Vector3.ZeroReadOnly,
            this._targetScene,
            false
        );
        camera.detachControl();
        return camera;
    }

    /**
     * 初始化容器网格
     * @param options 参数对象
     * @returns 网格
     */
    private initializeBody(options: IDetailViewOptions): AbstractMesh {
        const body = options.body || this.generateBody(this._scene, options);
        const container = options.container || body;
        container.billboardMode = options.billboard === undefined ? AbstractMesh.BILLBOARDMODE_ALL : options.billboard;

        const material = new StandardMaterial(this._name + '-body-material', this._scene);
        material.disableLighting = true;
        material.cullBackFaces = false;

        const rsl = options.textureResolution || new Vector2(512, 512);
        const rtt = new RenderTargetTexture(this._name + '-render-target-texture', {
            width: rsl.x,
            height: rsl.y,
        }, this._targetScene);

        rtt.refreshRate = options.refreshRate === undefined ? 1 : options.refreshRate;
        rtt.activeCamera = this._targetCamera;
        this._renderTargetTexture = rtt;
        this.setRenderTargetTexture(options);
        material.emissiveTexture = rtt;

        if (options.hasAlpha) {
            material.alphaMode = StandardMaterial.MATERIAL_ALPHABLEND;
            rtt.hasAlpha = true;
            material.opacityTexture = rtt;
        } else {
            material.alphaMode = StandardMaterial.MATERIAL_OPAQUE;
            rtt.hasAlpha = false;
        }

        this._scene.customRenderTargets.push(rtt);
        body.material = material;
        return body;
    }

    /**
     * 创建网格容器
     * @param scene 所在场景
     * @param ops 参数对象
     * @returns 网格
     */
    protected generateBody(scene: Scene, ops: IDetailViewOptions): AbstractMesh {
        const size = ops.size || 1;
        const tsl = ops.tessellation || 36;
        const bm = MeshBuilder.CreateDisc(this._name + '-body-mesh', {
            radius: size,
            tessellation: tsl,
            updatable: false,
        }, scene);
        return bm;
    }

    /**
     * 设置渲染对象纹理
     * @param options 参数对象
     */
    private setRenderTargetTexture(options: IDetailViewOptions): void {
        const rtt = this._renderTargetTexture;
        rtt.uAng = options.renderTargetTextureUAngle === undefined ? 3.1415926 : options.renderTargetTextureUAngle;
        rtt.vAng = options.renderTargetTextureVAngle === undefined ? 0 : options.renderTargetTextureVAngle;
        rtt.wAng = options.renderTargetTextureWAngle === undefined ? 0 : options.renderTargetTextureWAngle;
        this.windowResizeUpdate();
    }

}


export { DetailView };
export type { IDetailViewOptions };